/***************************************************************************
                         tgaload  -  Part of gltris
                             -------------------
    begin of project     : March 2001
    copyright            : (C) 2001 by David Delbecq (Alias Tchize)
    email                : david.delbecq@usa.net
 ***************************************************************************/
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/*

   NOTE:
       This tgaloader was ripped from an opengl tutorial and has been modified and simplified.
			 Those modifications include handling messages in tga files as explained in the specifications.
       Here follow original comments
   ...

   Aug 19th:
   Added support for runlength encoding - changed some stuff around to make this
   possible.  Works well :)

   Sept 7th:
   Improved error trapping and recovery.

   Oct 22nd:
   Major source clearout & Can compress the image using S3_TC algorithm if the
   driver supports it.

   Nov 10th:
   'Settled' version of the code - traps for nearly all errors - added a
   LoadAndBind function for even lazier people :)
   TGA_NO_PASS was added in case you need to load an image and pass it yourself.
   Finally exorcised all the paletted texture code...
*/

#include "os_depend.h"
#include "defs.h"
#include <GL/glu.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tgaload.h"

/* Extension Management */
/* Linux version: those constant are already defined in opengl libraries!!*/
/*PFNGLCOMPRESSEDTEXIMAGE2DARBPROC  glCompressedTexImage2DARB  = NULL;
  PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImageARB = NULL;*/

/* Default support - lets be optimistic! */
int tgaCompressedTexSupport = true;


void tgaGetExtensions ( void )
{  
/*   glCompressedTexImage2DARB  = ( PFNGLCOMPRESSEDTEXIMAGE2DARBPROC  )
                   wglGetProcAddress ( "glCompressedTexImage2DARB"  );
   glGetCompressedTexImageARB = ( PFNGLGETCOMPRESSEDTEXIMAGEARBPROC )
                   wglGetProcAddress ( "glGetCompressedTexImageARB" );

   if ( glCompressedTexImage2DARB == NULL || glGetCompressedTexImageARB == NULL )
   	tgaCompressedTexSupport = false;*/
}


void tgaSetTexParams  ( unsigned int min_filter, unsigned int mag_filter, unsigned int application )
{
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter );
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter );

   glTexEnvf ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, application );
}


unsigned char *tgaAllocMem ( tgaHeader_t info )
{
   unsigned char  *block;

   block = (unsigned char*) malloc ( info.bytes );

   if ( block == NULL )
      return 0;

   memset ( block, 0x00, info.bytes );

   return block;
}

void tgaPutPacketTuples ( image_t *p, unsigned char *temp_colour, int *current_byte )
{
   if ( p->info.components == 3 )
   {
      p->data[*current_byte]   = temp_colour[2];
      p->data[*current_byte+1] = temp_colour[1];
      p->data[*current_byte+2] = temp_colour[0];
      *current_byte += 3;
   }

   if ( p->info.components == 4 )    /* Because its BGR(A) not (A)BGR :(*/
   {
      p->data[*current_byte]   = temp_colour[2];
      p->data[*current_byte+1] = temp_colour[1];
      p->data[*current_byte+2] = temp_colour[0];
      p->data[*current_byte+3] = temp_colour[3];
      *current_byte += 4;
   }
}


void tgaGetAPacket ( int *current_byte, image_t *p, FILE *file )
{
   unsigned char  packet_header;
   int            run_length;
   int            i;
   unsigned char  temp_colour[4] = { 0x00, 0x00, 0x00, 0x00 };

   fread        ( &packet_header, ( sizeof ( unsigned char )), 1, file );
   run_length = ( packet_header&0x7F ) + 1;

   if ( packet_header&0x80 )  /* RLE packet*/
   {
      fread ( temp_colour, ( sizeof ( unsigned char )* p->info.components ), 1, file );

      if ( p->info.components == 1 )  /* Special optimised case :)*/
      {
         memset ( p->data + *current_byte, temp_colour[0], run_length );
         *current_byte += run_length;
      } else
      for ( i = 0; i < run_length; i++ )
         tgaPutPacketTuples ( p, temp_colour, current_byte );
   }

   if ( !( packet_header&0x80 ))  /* RAW packet*/
   {
      for ( i = 0; i < run_length; i++ )
      {
         fread ( temp_colour, ( sizeof ( unsigned char )* p->info.components ), 1, file );

         if ( p->info.components == 1 )
         {
            memset ( p->data + *current_byte, temp_colour[0], run_length );
            *current_byte += run_length;
         } else
            tgaPutPacketTuples ( p, temp_colour, current_byte );
      }
   }
}


void tgaGetPackets ( image_t *p, FILE *file )
{
  int current_byte = 0;

  while ( current_byte < p->info.bytes )
    tgaGetAPacket ( &current_byte, p, file );
}


void tgaGetImageData ( image_t *p, FILE *file )
{
   unsigned char  temp;
   int            i;

   p->data = tgaAllocMem ( p->info );

   /* Easy unRLE image */
   if ( p->info.image_type == 1 || p->info.image_type == 2 || p->info.image_type == 3 )
   {
      fread ( p->data, sizeof (unsigned char), p->info.bytes, file );

      /* Image is stored as BGR(A), make it RGB(A)     */
      for (i = 0; i < p->info.bytes; i += p->info.components )
      {
         temp = p->data[i];
         p->data[i] = p->data[i + 2];
         p->data[i + 2] = temp;
      }
   }

   /* RLE compressed image */
   if ( p->info.image_type == 9 || p->info.image_type == 10 )
      tgaGetPackets ( p, file );
}


void tgaUploadImage ( image_t *p, tgaFLAG mode )
{
   /*  Determine TGA_LOWQUALITY  internal format
       This directs OpenGL to upload the textures at half the bit
       precision - saving memory
    */
   GLenum internal_format = p->info.tgaColourType;
#if defined(GL_RGB4)&&defined(GL_RGBA4)&&defined(GL_LUMINANCE4)&&defined(GL_ALPHA4) 
	if ( mode&TGA_LOW_QUALITY )
   {
      switch ( p->info.tgaColourType )
      {
         case GL_RGB       : internal_format = GL_RGB4; break;
         case GL_RGBA      : internal_format = GL_RGBA4; break; 
         case GL_LUMINANCE : internal_format = GL_LUMINANCE4; break;
         case GL_ALPHA     : internal_format = GL_ALPHA4; break;          
      }
   }
#endif
#if defined(GL_COMPRESSED_RGB_ARB)&&defined(GL_COMPRESSED_RGBA_ARB)&&defined(GL_COMPRESSED_LUMINANCE_ARB)&&defined(GL_COMPRESSED_ALPHA_ARB)
   /*  Let OpenGL decide what the best compressed format is each case. */   
   if ( mode&TGA_COMPRESS && tgaCompressedTexSupport )
   {            
      switch ( p->info.tgaColourType )
      {
         case GL_RGB       : internal_format = GL_COMPRESSED_RGB_ARB; break;
         case GL_RGBA      : internal_format = GL_COMPRESSED_RGBA_ARB; break; 
         case GL_LUMINANCE : internal_format = GL_COMPRESSED_LUMINANCE_ARB; break; 
         case GL_ALPHA     : internal_format = GL_COMPRESSED_ALPHA_ARB; break;          
      }
   }
#endif
   /*  Pass OpenGL Texture Image */
   if ( !( mode&TGA_NO_MIPMAPS ))
      gluBuild2DMipmaps ( GL_TEXTURE_2D, internal_format, p->info.width,
                          p->info.height, p->info.tgaColourType, GL_UNSIGNED_BYTE, p->data );
   else
      glTexImage2D ( GL_TEXTURE_2D, 0, internal_format, p->info.width,
                     p->info.height, 0, p->info.tgaColourType, GL_UNSIGNED_BYTE, p->data );
}


void tgaFree ( image_t *p )
{
   if ( p->data != NULL )
      free ( p->data );
}


void tgaChecker ( image_t *p )
{
  unsigned char TGA_CHECKER[16384];
  unsigned char *pointer;
  int           i,j;

  /* 8bit image*/
  p->info.image_type = 3;

  p->info.width  = 128;
  p->info.height = 128;

  p->info.pixel_depth = 8;

  /* Set some stats*/
  p->info.components = 1;
  p->info.bytes      = p->info.width * p->info.height * p->info.components;

  pointer = TGA_CHECKER;

  for ( j = 0; j < 128; j++ )
  {
    for ( i = 0; i < 128; i++ )
    {
      if ((i ^ j) & 0x10 )
        pointer[0] = 0x00;
      else
        pointer[0] = 0xff;
      pointer ++;
    }
  }

  p->data = TGA_CHECKER;

  glTexImage2D ( GL_TEXTURE_2D, 0, GL_LUMINANCE, p->info.width,
                 p->info.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, p->data );

/*  Should we free?  I dunno.  The scope of TGA_CHECKER _should_ be local, so it
    probably gets destroyed automatically when the function completes... */
}


void tgaError ( char *error_string, char *file_name, FILE *file, image_t *p )
{
   tgaFree ( p );
   fclose ( file );
   tgaChecker ( p );
   printf ("\n[%s]\n",error_string);
}


void tgaGetImageHeader ( FILE *file, tgaHeader_t *info )
{
   /*   Stupid byte alignment means that we have to fread each field
        individually.  I tried splitting tgaHeader into 3 structures, no matter
        how you arrange them, colour_map_entry_size comes out as 2 bytes instead
        1 as it should be.  Grrr.  Gotta love optimising compilers - theres a pragma
        for Borland, but I dunno the number for MSVC or GCC :(
    */
   char buffer[256];
   fread ( &info->id_length,       ( sizeof (unsigned char )), 1, file );
   fread ( &info->colour_map_type, ( sizeof (unsigned char )), 1, file );
   fread ( &info->image_type,      ( sizeof (unsigned char )), 1, file );

   fread ( &info->colour_map_first_entry, ( sizeof (short int )), 1, file );
   fread ( &info->colour_map_length     , ( sizeof (short int )), 1, file );
   fread ( &info->colour_map_entry_size , ( sizeof (unsigned char )), 1, file );

   fread ( &info->x_origin , ( sizeof (short int )), 1, file );
   fread ( &info->y_origin , ( sizeof (short int )), 1, file );
   fread ( &info->width,     ( sizeof (short int )), 1, file );
   fread ( &info->height,    ( sizeof (short int )), 1, file );

   fread ( &info->pixel_depth,     ( sizeof (unsigned char )), 1, file );
   fread ( &info->image_descriptor,( sizeof (unsigned char )), 1, file );

   /* Check to see if there's a comment, read it and sent to console*/
   if (info->id_length!=0)
        {
        fread (buffer,(sizeof (unsigned char)), info->id_length, file);
        buffer[info->id_length]='\0';
        printf ("%s",buffer);
        }

   /* Set some stats*/
   info->components = info->pixel_depth / 8;
   info->bytes      = info->width * info->height * info->components;

}

int tgaLoadTheImage ( char *file_name, image_t *p, tgaFLAG mode )
{
   FILE   *file;

   tgaGetExtensions ( );

   p->data = NULL;

   if (( file = fopen ( file_name, "rb" )) == NULL )
   {
   	tgaError ( "File not found", file_name, file, p );
      return 0;
   }

   tgaGetImageHeader ( file, &p->info );

   switch ( p->info.image_type )
   {
      case 1 :
         tgaError ( "8-bit colour no longer supported", file_name, file, p );
         return 0;

      case 2 :
         if ( p->info.pixel_depth == 24 )
            p->info.tgaColourType = GL_RGB;
         else if ( p->info.pixel_depth == 32 )
            p->info.tgaColourType = GL_RGBA;
			else
	      {
         	tgaError ( "Unsupported RGB format", file_name, file, p );
         	return 0;
         }
         break;

      case 3 :
         if ( mode&TGA_LUMINANCE )
            p->info.tgaColourType = GL_LUMINANCE;
         else if ( mode&TGA_ALPHA )
            p->info.tgaColourType = GL_ALPHA;
      	else
	      {
         	tgaError ( "Must be LUMINANCE or ALPHA greyscale", file_name, file, p );
				return 0;
         }
         break;

      case 9 :
         tgaError ( "8-bit colour no longer supported", file_name, file, p );
         return 0;
         break;

      case 10 :
         if ( p->info.pixel_depth == 24 )
            p->info.tgaColourType = GL_RGB;
         else if ( p->info.pixel_depth == 32 )
            p->info.tgaColourType = GL_RGBA;
         else
	      {
         	tgaError ( "Unsupported compressed RGB format", file_name, file, p );
         	return 0;
         }

   }
   tgaGetImageData ( p, file );

   fclose  ( file );

   return 1;
}

void tgaLoad ( char *file_name, image_t *p, tgaFLAG mode )
{
	if ( tgaLoadTheImage ( file_name, p, mode ))
   {
   	if  ( !( mode&TGA_NO_PASS ))
     	 tgaUploadImage  ( p, mode );

   	if ( mode&TGA_FREE )
     		tgaFree ( p );
   }

}

GLuint tgaLoadAndBind ( char *file_name, tgaFLAG mode )
{
   GLuint   texture_id;
   image_t  *p;

   glGenTextures ( 1, &texture_id );
   glBindTexture ( GL_TEXTURE_2D, texture_id );

   if ( tgaLoadTheImage ( file_name, p, mode ))
	{
   	tgaUploadImage  ( p, mode );
   	tgaFree       ( p );
   }

   return texture_id;
}

